package fightplan.algorithmen.ko;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import fightplan.algorithmen.SeedComparator;
import fightplan.business.AbstractFighter;
import fightplan.business.Category;
import fightplan.business.Fight;
import fightplan.business.FighterHolder;
import fightplan.business.FighterProvider;
import fightplan.business.Winner;

public class KOPlanFactory {

	public KOPlan plan(Category category, boolean trostrunde) {
		KOPlan plan = new KOPlan(trostrunde);
		return plan(plan, category, trostrunde);
	}

	protected KOPlan plan(KOPlan plan, Category category, boolean trostrunde) {
		List<AbstractFighter> fighters = new ArrayList<AbstractFighter>(category.getFighters());
		Collections.sort(fighters, new SeedComparator(category));

		Fight ffinal = plan(plan, category, 0, fighters, null);
		ffinal.setName("Final");

		plan.getWinners().add(new Winner(1, ffinal.getResultHolder(0)));
		plan.getWinners().add(new Winner(2, ffinal.getResultHolder(1)));

		if (trostrunde) {
			// depth: Mindestens 3 Stufen angezeigt, aber ev. mehr
			// bis 32 : 3
			// bis 64 : 4
			// bis 128 : 5
			// usw...

			int depth = 3;
			int f = 32;
			while (f < fighters.size()) {
				depth++;
				f = f * 2;
			}

			planTrostRunde(plan, true, depth);
			planTrostRunde(plan, false, depth);

			plan.getWinners().add(new Winner(3, plan.getUpperTrostRundeFights().get(depth - 1).getWinResultHolder()));
			plan.getWinners().add(new Winner(3, plan.getLowerTrostRundeFights().get(depth - 1).getWinResultHolder()));
		}

		return plan;
	}

	private static Fight plan(KOPlan plan, Category category, int level, List<AbstractFighter> fighters,
			List<FighterHolder> trostRundenFighters) {
		if (fighters.size() < 2)
			throw new IllegalArgumentException();

		// Als erstes werden zwei Haufen an Teilnehmern gebildet, wobei
		// immer zwei möglichst aehnliche gesucht werden und die dann auf die
		// zwei
		// Haufen verteilt werden.
		//
		// Es wird davon ausgegangen, dass die fighters-Liste nach der
		// Gesetztenliste sortiert ist.

		List<AbstractFighter> used = new ArrayList<AbstractFighter>();
		List<AbstractFighter> list1 = new ArrayList<AbstractFighter>();
		List<AbstractFighter> list2 = new ArrayList<AbstractFighter>();
		boolean toOne = true;
		for (int i = 0; i < fighters.size(); i++) {
			AbstractFighter fighter = fighters.get(i);
			if (used.contains(fighter))
				continue;
			used.add(fighter);

			AbstractFighter fighter2 = null;
			for (int j = i + 1; j < fighters.size(); j++) {
				fighter2 = fighters.get(j);
				if (used.contains(fighter2)) {
					fighter2 = null;
					continue;
				}
				if (fighter.getDojo() == fighter2.getDojo() || fighter.getDojo() != null
						&& fighter.getDojo().equals(fighter2.getDojo()) || category.getSeeds().contains(fighter)) {
					used.add(fighter2);
					break;
				} else {
					fighter2 = null;
				}
			}
			if (toOne) {
				list1.add(fighter);
				if (fighter2 != null)
					list2.add(fighter2);
				toOne = false;
			} else {
				list2.add(fighter);
				if (fighter2 != null)
					list1.add(fighter2);
				toOne = true;
			}

			// Zusätzlich noch überprüfen, ob eine Schräglage zwischen den zwei
			// Listen
			// entstanden ist. Das kann bei den Gesetzten noch nicht passieren,
			// aber
			// danach, wenn nur noch auf die Dojo-Namen geschaut wird.
			if (list1.size() > list2.size()) {
				toOne = false;
			} else if (list2.size() > list1.size()) {
				toOne = true;
			}
		}

		Fight fight = plan.addFight();

		List<FighterHolder> trostRundenFighters1;
		List<FighterHolder> trostRundenFighters2;
		if (trostRundenFighters == null) {
			trostRundenFighters1 = new ArrayList<FighterHolder>();
			trostRundenFighters2 = new ArrayList<FighterHolder>();
		} else {
			trostRundenFighters1 = trostRundenFighters;
			trostRundenFighters2 = trostRundenFighters;
		}

		if (list1.size() == 0) {
			System.out.println("Error list 1 in CategoryByDojo.layout");
		} else if (list1.size() == 1) {
			fight.addFighter(list1.get(0));
		} else {

			Fight fight1 = plan(plan, category, level + 1, list1, trostRundenFighters1);

			FighterHolder fighterHolder = fight1.getWinResultHolder();
			fight.addFighter(fighterHolder);
		}

		if (list2.size() == 0) {
			System.out.println("Error list 2 in CategoryByDojo.layout");
		} else if (list2.size() == 1) {
			fight.addFighter(list2.get(0));
		} else {
			Fight fight2 = plan(plan, category, level + 1, list2, trostRundenFighters2);

			FighterHolder fighterHolder = fight2.getWinResultHolder();
			fight.addFighter(fighterHolder);
		}

		return fight;
	}

	private static void planTrostRunde(KOPlan plan, boolean upper, int depth) {
		Fight rootFight = plan.getRootFight();

		FighterProvider fighterProvider = rootFight.getFightEntries().get(upper ? 0 : 1).getFighterProvider();
		if (fighterProvider instanceof FighterHolder) {
			FighterHolder fighterHolder = (FighterHolder) fighterProvider;
			List<Fight> trostRundenFights = upper ? plan.getUpperTrostRundeFights() : plan.getLowerTrostRundeFights();

			Fight trostRundeRootFight = fighterHolder.getFight();
			TrostRunde trostRunde = new TrostRunde(trostRundeRootFight);

			Fight fight = plan.addFight();
			trostRundenFights.add(fight);
			fight.setName("Trostrunde");
			fight.addFighter(trostRunde.getLooser(depth--));
			fight.addFighter(trostRunde.getLooser(depth--));
			for (int i = depth; i >= 0; i--) {
				Fight nextFight = plan.addFight();
				trostRundenFights.add(nextFight);
				nextFight.setName("Trostrunde");
				nextFight.addFighter(trostRunde.getLooser(i));
				nextFight.addFighter(fight.getWinResultHolder());
				fight = nextFight;
			}

		}
	}

}